package cc.shacocloud.mirage.utils;

import cc.shacocloud.mirage.utils.collection.ArrayUtil;
import cc.shacocloud.mirage.utils.map.ConcurrentReferenceHashMap;
import cc.shacocloud.mirage.utils.reflection.ReflectUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.reflect.*;

/**
 * {@link #forField(Field) Fields}或{@link #forMethodParameter(MethodParameter) MethodParameters}
 * 可以作为可序列化类型的根来源。
 * <p>
 * 返回的类型要么是{@link Class}，要么是{@link GenericArrayType}、{@link ParameterizedType}、{@link TypeVariable}或
 * {@link WildcardType}的可序列化代理。除了{@link Class}（它是最终的），对返回进一步的{@link Type Types}
 * （例如 {@link GenericArrayType#getGenericComponentType()}）的方法的调用将被自动包装。
 */
final class SerializableTypeWrapper {
    
    static final ConcurrentReferenceHashMap<Type, Type> cache = new ConcurrentReferenceHashMap<>(256);
    private static final Class<?>[] SUPPORTED_SERIALIZABLE_TYPES = {
            GenericArrayType.class, ParameterizedType.class, TypeVariable.class, WildcardType.class};
    
    
    private SerializableTypeWrapper() {
    }
    
    
    /**
     * 返回 {@link Serializable} 变体 {@link Field#getGenericType}
     */
    @Nullable
    public static Type forField(Field field) {
        return forTypeProvider(new FieldTypeProvider(field));
    }
    
    /**
     * 返回 {@link Serializable} 变体 {@link MethodParameter#getGenericParameterType}
     */
    @Nullable
    public static Type forMethodParameter(MethodParameter methodParameter) {
        return forTypeProvider(new MethodParameterTypeProvider(methodParameter));
    }
    
    /**
     * 解开给定类型的包装，有效地返回原始的不可序列化类型
     *
     * @param type 要解包的类型
     * @return 原始不可序列化类型
     */
    @SuppressWarnings("unchecked")
    public static <T extends Type> T unwrap(T type) {
        Type unwrapped = null;
        if (type instanceof SerializableTypeProxy) {
            unwrapped = ((SerializableTypeProxy) type).getTypeProvider().getType();
        }
        return (unwrapped != null ? (T) unwrapped : type);
    }
    
    /**
     * 返回由 {@link TypeProvider} 支持的 {@link Serializable} {@link Type}
     * <p>
     * 如果类型项目在当前运行时环境中通常不可序列化，则此委托将仅按原样返回原始 {@code Type}
     */
    @Nullable
    static Type forTypeProvider(@NotNull TypeProvider provider) {
        Type providedType = provider.getType();
        if (providedType == null || providedType instanceof Serializable) {
            // 没有必要对可序列化的类型进行包装（例如对于java.lang.Class）。
            return providedType;
        }
        
        if (NativeDetector.inNativeImage() || !Serializable.class.isAssignableFrom(Class.class)) {
            // 如果类型在当前的运行环境中一般是不可序列化的（甚至是java.lang.Class本身，例如在GraalVM的本地图像上），
            // 我们就跳过任何封装的尝试。
            return providedType;
        }
        
        // 为给定的提供者获取一个可序列化的类型代理...
        Type cached = cache.get(providedType);
        if (cached != null) {
            return cached;
        }
        
        for (Class<?> type : SUPPORTED_SERIALIZABLE_TYPES) {
            if (type.isInstance(providedType)) {
                ClassLoader classLoader = provider.getClass().getClassLoader();
                Class<?>[] interfaces = new Class<?>[]{type, SerializableTypeProxy.class, Serializable.class};
                InvocationHandler handler = new TypeProxyInvocationHandler(provider);
                cached = (Type) Proxy.newProxyInstance(classLoader, interfaces, handler);
                cache.put(providedType, cached);
                return cached;
            }
        }
        throw new IllegalArgumentException("不支持的类型类: " + providedType.getClass().getName());
    }
    
    
    /**
     * 由类型代理实现的附加接口
     */
    interface SerializableTypeProxy {
        
        /**
         * 返回底层类型提供者。
         */
        TypeProvider getTypeProvider();
    }
    
    
    /**
     * 一个{@link Serializable}接口提供对{@link Type}的访问。
     */
    @SuppressWarnings("serial")
    interface TypeProvider extends Serializable {
        
        /**
         * 返回（可能是非{@link Serializable}）{@link Type}。
         */
        @Nullable
        Type getType();
        
        /**
         * 返回该类型的来源，如果不知道，则返回{@code null}。
         * <p>
         * 默认的实现返回{@code null}。
         */
        @Nullable
        default Object getSource() {
            return null;
        }
    }
    
    
    /**
     * {@link Serializable} {@link InvocationHandler}由代理的{@link Type}使用。
     * 提供序列化支持，并增强任何返回{@code Type} * 或{@code Type[]}的方法。
     */
    @SuppressWarnings("serial")
    private static class TypeProxyInvocationHandler implements InvocationHandler, Serializable {
        
        private final TypeProvider provider;
        
        public TypeProxyInvocationHandler(TypeProvider provider) {
            this.provider = provider;
        }
        
        @Override
        @Nullable
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            switch (method.getName()) {
                case "equals":
                    Object other = args[0];
                    // Unwrap proxies for speed
                    if (other instanceof Type) {
                        other = unwrap((Type) other);
                    }
                    return Utils.nullSafeEquals(this.provider.getType(), other);
                case "hashCode":
                    return Utils.nullSafeHashCode(this.provider.getType());
                case "getTypeProvider":
                    return this.provider;
            }
            
            if (Type.class == method.getReturnType() && ArrayUtil.isEmpty(args)) {
                return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1));
            } else if (Type[].class == method.getReturnType() && ArrayUtil.isEmpty(args)) {
                Type[] result = new Type[((Type[]) method.invoke(this.provider.getType())).length];
                for (int i = 0; i < result.length; i++) {
                    result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i));
                }
                return result;
            }
            
            try {
                return method.invoke(this.provider.getType(), args);
            } catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            }
        }
    }
    
    
    /**
     * {@link TypeProvider}用于从{@link Type Types}获得的{@link Field}。
     */
    static class FieldTypeProvider implements TypeProvider {
        
        private final String fieldName;
        
        private final Class<?> declaringClass;
        
        private transient Field field;
        
        public FieldTypeProvider(@NotNull Field field) {
            this.fieldName = field.getName();
            this.declaringClass = field.getDeclaringClass();
            this.field = field;
        }
        
        @Override
        public Type getType() {
            return this.field.getGenericType();
        }
        
        @Override
        public Object getSource() {
            return this.field;
        }
        
        private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
            inputStream.defaultReadObject();
            try {
                this.field = this.declaringClass.getDeclaredField(this.fieldName);
            } catch (Throwable ex) {
                throw new IllegalStateException("无法找到原始的类结构", ex);
            }
        }
    }
    
    
    /**
     * {@link TypeProvider}用于从{@link MethodParameter}获得的{@link Type Types}
     */
    @SuppressWarnings("serial")
    static class MethodParameterTypeProvider implements TypeProvider {
        
        @Nullable
        private final String methodName;
        
        private final Class<?>[] parameterTypes;
        
        private final Class<?> declaringClass;
        
        private final int parameterIndex;
        
        private transient MethodParameter methodParameter;
        
        public MethodParameterTypeProvider(MethodParameter methodParameter) {
            this.methodName = (methodParameter.getMethod() != null ? methodParameter.getMethod().getName() : null);
            this.parameterTypes = methodParameter.getExecutable().getParameterTypes();
            this.declaringClass = methodParameter.getDeclaringClass();
            this.parameterIndex = methodParameter.getParameterIndex();
            this.methodParameter = methodParameter;
        }
        
        @Override
        public Type getType() {
            return this.methodParameter.getGenericParameterType();
        }
        
        @Override
        public Object getSource() {
            return this.methodParameter;
        }
        
        private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
            inputStream.defaultReadObject();
            try {
                if (this.methodName != null) {
                    this.methodParameter = new MethodParameter(
                            this.declaringClass.getDeclaredMethod(this.methodName, this.parameterTypes), this.parameterIndex);
                } else {
                    this.methodParameter = new MethodParameter(
                            this.declaringClass.getDeclaredConstructor(this.parameterTypes), this.parameterIndex);
                }
            } catch (Throwable ex) {
                throw new IllegalStateException("无法找到原始的类结构", ex);
            }
        }
    }
    
    
    /**
     * {@link TypeProvider}为{@link Type Types}通过调用一个无参数的方法获得。
     */
    static class MethodInvokeTypeProvider implements TypeProvider {
        
        private final TypeProvider provider;
        
        private final String methodName;
        
        private final Class<?> declaringClass;
        
        private final int index;
        
        private transient Method method;
        
        @Nullable
        private transient volatile Object result;
        
        public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) {
            this.provider = provider;
            this.methodName = method.getName();
            this.declaringClass = method.getDeclaringClass();
            this.index = index;
            this.method = method;
        }
        
        @Override
        @Nullable
        public Type getType() {
            Object result = this.result;
            if (result == null) {
                // 对提供的类型延迟调用目标方法
                result = ReflectUtil.invokeMethod(this.method, this.provider.getType());
                this.result = result;
            }
            return (result instanceof Type[] ? ((Type[]) result)[this.index] : (Type) result);
        }
        
        @Override
        @Nullable
        public Object getSource() {
            return null;
        }
        
        private void readObject(@NotNull ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
            inputStream.defaultReadObject();
            Method method = ReflectUtil.getMethod(this.declaringClass, this.methodName);
            if (method == null) {
                throw new IllegalStateException("无法找到反序列化的方法: " + this.methodName);
            }
            if (method.getReturnType() != Type.class && method.getReturnType() != Type[].class) {
                throw new IllegalStateException("反序列化方法的无效返回类型--需要是Type或Type[]: " + method);
            }
            this.method = method;
        }
    }
    
}
